package com.example.supernote.ui.note

import android.net.Uri
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.supernote.data.entity.FileEntity
import com.example.supernote.data.network.AudioApi
import com.example.supernote.data.network.ImageApi
import com.example.supernote.data.network.ImageOCRApi
import com.example.supernote.data.network.TextGenerationApi
import com.example.supernote.data.network.TranslateTextApi
import com.example.supernote.data.network.config.CreateAudioBody
import com.example.supernote.data.network.config.CreateAudioResponse
import com.example.supernote.data.network.config.ImageOCRRequestBody
import com.example.supernote.data.network.config.ImageOCRResponse
import com.example.supernote.data.network.config.OcrResult
import com.example.supernote.data.network.config.ProgressAudioBody
import com.example.supernote.data.network.config.ProgressAudioResponse
import com.example.supernote.data.network.config.Response
import com.example.supernote.data.network.config.ResponseData
import com.example.supernote.data.network.config.ResultAudioBody
import com.example.supernote.data.network.config.ResultAudioResponse
import com.example.supernote.data.network.config.RunAudioBody
import com.example.supernote.data.network.config.RunAudioResponse
import com.example.supernote.data.network.config.TextData
import com.example.supernote.data.network.config.TextGeneration
import com.example.supernote.data.network.config.TextPrompt
import com.example.supernote.data.network.config.TranslateTextRequestBody
import com.example.supernote.data.network.config.TranslateTextResponse
import com.example.supernote.data.network.config.UploadAudioResponse
import com.example.supernote.data.network.config.generateAuthHeaders
import com.example.supernote.data.network.config.getRequestBody
import com.example.supernote.data.network.config.getUrlParams
import com.example.supernote.data.repository.FileEntityRepository
import com.example.supernote.model.note.commands.AbstractCommand
import com.example.supernote.model.note.context.Context
import com.example.supernote.model.note.operations.AbstractOpration
import com.example.supernote.model.note.operations.InsertCommandGroup
import com.example.supernote.model.note.operations.LayoutCommandGroup
import com.example.supernote.model.note.operations.MoreCommandGroup
import com.example.supernote.model.note.operations.TextCommandGroup
import com.example.supernote.ui.components.UniformToast
import com.example.supernote.ui.note.utils.Note
import com.example.supernote.ui.note.utils.changeToText
import com.example.supernote.ui.note.utils.dealResultAudioResponse
import com.example.supernote.ui.note.utils.getFileAppendnameByUri
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.Date
import java.util.UUID

data class NoteUiState(
    // 变量
    var disableCommands: Boolean = false,
    var editMode: Boolean = true,
    var isSuccessGenImage: Boolean = false,
    var curOperation: String = "",
    var note: Note? = null,
    // 不变量
    val opsTitle: Map<String, AbstractOpration> = mapOf(
        "文本" to TextCommandGroup(),
        "布局" to LayoutCommandGroup(),
        "插入" to InsertCommandGroup(),
        "更多" to MoreCommandGroup()
    )
)

class NoteViewModel(
    private var previousHistory: MutableList<Context> = mutableListOf(),
    private var postHistory: MutableList<Context> = mutableListOf(),
    private val fileEntityRepository: FileEntityRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(NoteUiState())
    val uiState: StateFlow<NoteUiState> = _uiState.asStateFlow()

    suspend fun fetchFile(id: Int) {
        val fileEntity = fileEntityRepository.getFileEntityStream(id)
        _uiState.update {
            it.copy(
                note = Note(
                    id = fileEntity.id,
                    title = fileEntity.name,
                    context = Context(
                        article = TextFieldValue(fileEntity.text)
                    ),
                    lastModify = fileEntity.lastModified,
                    path = fileEntity.path
                ),
                curOperation = "",
                editMode = true,
                disableCommands = false
            )
        }
    }

    // 保存
    suspend fun saveFile() {
        val curNote = _uiState.value.note!!.copy()
        curNote.lastModify = Date().time
        val fileEntity = FileEntity(
            id = curNote.id,
            path = curNote.path,
            name = curNote.title,
            lastModified = curNote.lastModify,
            text = curNote.context.article.text
        )
        // 更新界面
        _uiState.update { it.copy(note = curNote, curOperation = "", editMode = false) }
        // 更新数据库
        fileEntityRepository.updateFileEntity(fileEntity)
    }

    // 编辑文本内容
    fun editContext(newContext: TextFieldValue, needAddHistory: Boolean = true) {
        val newNote = _uiState.value.note?.copy()
        if (needAddHistory && newNote!!.context.article.text != newContext.text) {
            previousHistory.add(newNote.context.copy())
            postHistory.clear()
        }
        newNote!!.context.article = newContext
        newNote.context.selection = newContext.selection
        _uiState.update { it.copy(note = newNote, disableCommands = false) }
    }

    // 编辑文本标题
    fun editTitle(newTitle: String) {
        val newNote = _uiState.value.note?.copy()
        newNote!!.title = newTitle
        _uiState.update { it.copy(note = newNote, curOperation = "") }
    }

    // 编辑模式
    fun changeEditMode() {
        _uiState.update { it.copy(editMode = !it.editMode, curOperation = "") }
    }

    // 子列表
    fun changeOperation(choice: String) {
        _uiState.update { it.copy(curOperation = if (it.curOperation == choice) "" else choice) }
    }

    // EditManage
    fun execute(command: AbstractCommand, context: Context) {
        previousHistory.add(context)
        editContext(command.execute(context))
    }

    // 撤销
    fun undo() {
        if (previousHistory.isNotEmpty()) {
            val last = previousHistory.last()
            previousHistory.removeAt(previousHistory.size - 1)
            postHistory.add(_uiState.value.note!!.context.copy())
            editContext(last.article, false)
        }
    }

    // 重做
    fun redo() {
        if (postHistory.isNotEmpty()) {
            val last = postHistory.last()
            postHistory.removeAt(postHistory.size - 1)
            previousHistory.add(_uiState.value.note!!.context.copy())
            editContext(last.article, false)
        }
    }

    // ai
    suspend fun apiByPrompt(
        updateTemplate: (String) -> Unit,
        otherInfos: Map<String, String>
    ) {
        val requestId = UUID.randomUUID()
        val request = TextPrompt(
            prompt = otherInfos["prompt"]!!,
            systemPrompt = otherInfos["systemPrompt"]!!
        )
        var response = TextGeneration(
            code = -1,
            data = TextData(),
            msg = "网络错误，请重试"
        )
        try {
            response = TextGenerationApi.retrofitService.generateText(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = TextGenerationApi.GENERATE_TEXT_URI,
                    queryParams = "requestId=${requestId}"
                ),
                requestId = requestId.toString(),
                request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        val templateGenerated = if (response.code == 0)
            response.data.content
        else
            response.msg
        updateTemplate(templateGenerated.trim())
    }

    // 图像生成
    suspend fun imageGenerateApiByPrompt(
        updateImageUrl: (String) -> Unit,
        otherInfos: Map<String, String>
    ) {
        var response = Response(
            code = -1,
            result = ResponseData(),
            msg = "网络错误，请重试"
        )
        try {
            response = ImageApi.ImageGenerationApi.retrofitService.generateImage(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = ImageApi.ImageGenerationApi.GENERATE_IMAGE_URI,
                    queryParams = ""
                ),
                getRequestBody(prompt = otherInfos["prompt"]!!, styleConfig = otherInfos["systemPrompt"]!!)
            )
        } catch (e: Exception) {
            println("Error!! $e")
        }
        if (response.code == 200) {
            var times = 0
            while (times < 10 && !_uiState.value.isSuccessGenImage) {
                imageQueryApiByPrompt(updateImageUrl, response.result.task_id)
                delay(1000L)
                times += 1
            }
            if (times >= 10) {
                updateImageUrl("网络错误，请重试")
            }
        } else {
            updateImageUrl("网络错误，请重试")
        }
        _uiState.update { it.copy(isSuccessGenImage = false) }
    }

    private suspend fun imageQueryApiByPrompt(updateImageUrl: (String) -> Unit, taskId: String) {
        var response = Response(
            code = -1,
            result = ResponseData(),
            msg = "网络错误，请重试"
        )
        try {
            response = ImageApi.ImageQueryApi.retrofitService.queryImage(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "GET",
                    uri = ImageApi.ImageQueryApi.QUERY_IMAGE_URI,
                    queryParams = "task_id=${taskId}"
                ),
                task_id = taskId
            )
        } catch (e: Exception) {
            println("Error!! $e")
        }
        if (response.code == 200 && response.result.status == 2) {
            _uiState.update { it.copy(isSuccessGenImage = true) }
            updateImageUrl(response.result.images_url[0].trim())
        }
    }

    // 文本识别
    suspend fun imageOCRApi(
        resultImageOcr: (String) -> Unit,
        imageBase64: String,
        endLine: String
    ) {
        val request = ImageOCRRequestBody(
            image = imageBase64
        )
        var response = ImageOCRResponse(
            error_code = -1,
            error_msg = "网络错误，请重试",
            result = OcrResult()
        )
        try {
            response = ImageOCRApi.retrofitService.getOCRResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = ImageOCRApi.IMAGE_OCR_URI,
                    queryParams = ""
                ),
                request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        val templateGenerated: String = if (response.error_code == 0)
            changeToText(response.result.OCR, response.result.angle, endLine)
        else
            response.error_msg

        resultImageOcr(templateGenerated.trim())
    }

    // 文本翻译
    suspend fun translateTextApi(update: (String) -> Unit, otherInfos: Map<String, String>) {
        val requestId = UUID.randomUUID()
        val request = TranslateTextRequestBody(otherInfos["from"]!!, otherInfos["target"]!!, otherInfos["text"]!!, requestId.toString())
        var response = TranslateTextResponse()
        try {
            response = TranslateTextApi.retrofitService.getTranslateResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = TranslateTextApi.TRANSLATE_TEXT_URI,
                    queryParams = ""
                ),
                request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        val templateGenerated = if (response.code == 0)
            response.data["translation"]!!
        else
            response.msg
        update(templateGenerated.trim())
    }

    private suspend fun createAudioApi(audioType: String, sliceNum: String): CreateAudioResponse {
        val requestId = UUID.randomUUID()
        val request = CreateAudioBody(audioType, requestId.toString(), sliceNum)
        var response = CreateAudioResponse()
        try {
            response = AudioApi.CreateAudioApi.retrofitService.getCreateAudioResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = AudioApi.CreateAudioApi.CREATE_AUDIO_URI,
                    queryParams = ""
                ),
                getUrlParams(),
                request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        response.data["x-sessionId"] = requestId.toString()
        return response
    }

    private suspend fun uploadAudioApi(
        sliceIndex: String,
        audioData: ByteArray,
        infos: MutableMap<String, String>,
        filename: String
    ): Int {
        val paramsMap = getUrlParams(infos["audio_id"]!!, infos["x-sessionId"]!!, sliceIndex)
        var response = UploadAudioResponse()

        val requestBody = audioData.toRequestBody("application/octet-stream".toMediaTypeOrNull())
        val filePart = MultipartBody.Part.createFormData("file", filename, requestBody)

        try {
            response = AudioApi.UploadAudioApi.retrofitService.getUploadAudioResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = AudioApi.UploadAudioApi.UPLOAD_AUDIO_URI,
                    queryParams = ""
                ),
                paramsMap,
                filePart
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        return response.code
    }

    private suspend fun runAudioApi(infos: Map<String, String>): String {
        var response = RunAudioResponse()
        try {
            response = AudioApi.RunAudioApi.retrofitService.getRunAudioResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = AudioApi.RunAudioApi.RUN_AUDIO_URI,
                    queryParams = ""
                ),
                getUrlParams(),
                RunAudioBody(infos["audio_id"]!!, infos["x-sessionId"]!!).getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        return response.data["task_id"]!!
    }

    private suspend fun progressAudioApi(taskId: String, xSessionId: String): Int {
        var response = ProgressAudioResponse()
        try {
            response = AudioApi.ProgressAudioApi.retrofitService.getProgressAudioResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = AudioApi.ProgressAudioApi.PROGRESS_AUDIO_URI,
                    queryParams = ""
                ),
                getUrlParams(),
                ProgressAudioBody(taskId, xSessionId).getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        println("progress success: $response")

        return if (response.code == 0) response.data["progress"]!!
        else -1
    }

    private suspend fun resultAudioApi(taskId: String, xSessionId: String): ResultAudioResponse {
        var response = ResultAudioResponse()
        try {
            response = AudioApi.ResultAudioApi.retrofitService.getResultAudioResult(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = AudioApi.ResultAudioApi.RESULT_AUDIO_URI,
                    queryParams = ""
                ),
                getUrlParams(),
                ResultAudioBody(taskId, xSessionId).getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        return response
    }

    fun audioApi(
        updateTemplate: (String) -> Unit,
        updateProgress: (Float) -> Unit,
        audioUri: Uri,
        context: android.content.Context
    ) {
        CoroutineScope(Dispatchers.Default).launch {
            val chunkSize = 5 * 1024 * 1024
            try {
                val inputStream = context.contentResolver.openInputStream(audioUri)!!
                // 文件名称/类型/分片数量
                val fileAppendName = getFileAppendnameByUri(context, audioUri)
                val audioType = when (fileAppendName) {
                    "pcm" -> "pcm"
                    "wav", "m4a", "mp3", "acc", "ogg", "ogg_opus" -> "auto"
                    else -> null
                }
                audioType?.let {
                    val sliceNum =
                        (inputStream.available().toLong() + chunkSize - 1) / chunkSize
                    val infos = createAudioApi(audioType, sliceNum.toString())
                    if (infos.code == 0) {
                        var indice = 0
                        var hasError = false
                        val buffer = ByteArray(chunkSize)
                        var bytesRead: Int
                        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                            val chunk = buffer.copyOf(bytesRead)
                            updateProgress((indice + 1)/(sliceNum * 2).toFloat())
                            if (uploadAudioApi(indice.toString(), chunk, infos.data, "ttt.$fileAppendName") == 0) {
                                indice += 1
                            }
                            else {
                                hasError = true
                                break
                            }
                        }
                        inputStream.close()
                        if (!hasError) {
                            val taskId = runAudioApi(infos.data)
                            if (taskId.isNotEmpty()) {
                                var curProgress: Int
                                while (true) {
                                    curProgress = progressAudioApi(taskId, infos.data["x-sessionId"]!!)
                                    updateProgress((0.5 + curProgress/200.0).toFloat())
                                    if (curProgress == 100 || curProgress == -1) break
                                    delay(2000L)
                                }
                                if (curProgress == 100) {
                                    println("progress audio success!")
                                    val result = resultAudioApi(taskId, infos.data["x-sessionId"]!!)
                                    if (result.code == 0) {
                                        updateTemplate(dealResultAudioResponse(result.data["result"]!!))
                                    }
                                }
                            } else {
                                throw Exception("parse audio error!")
                            }
                        }
                    } else {
                        throw Exception("upload audio error")
                    }
                }?:run {
                    updateTemplate("Format Error")
                }
            } catch (e: Exception) {
                updateTemplate("Network Error")
                println("#########MyException!!!!!!!!!!!!!!${e}")
            }

        }
    }

    /**
     * Factory for InterestsViewModel that takes PostsRepository as a dependency
     */
    companion object {
        fun provideFactory(
            fileEntityRepository: FileEntityRepository
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return NoteViewModel(fileEntityRepository = fileEntityRepository) as T
            }
        }
    }
}
